# -*- coding:utf-8 -*-

"""
=============================
this is a feibot product,
all rights reserved.
www.feibot.com
@author: Gu Tianyuan
@email: g@feibot.com
@time: ${DATE}:${TIME}
@product: ${PRODUCT_NAME}
=============================
"""
import datetime
import random
import re
import threading
import time
import traceback
import urllib.error
import urllib.parse
import urllib.request
from multiprocessing import *
import requests
import json
from gtyIO import RUFUSapi
import gtySocket.socketTools
import gtyTools.gitTools

from gtyTools import gtyLog, tools, gtyTypes
from . import gtyIoTools
import os
import recordFileHandle
from gtyConfig import systemConfig
from datetime import datetime, timezone


class GtyIO:

    def __init__(self, eventQ):

        # 事件队列

        self.eventQ = eventQ
        # 配置的控制接口
        self.configHandlers = gtyTypes.ConfigHandlers()

        # 线程创建时
        self.machineId = tools.getMachineId(self.configHandlers.mmc.read("machine", "machineId"))

        self.eventId = self.configHandlers.event.read("event", "eventId")
        self.eventname = self.configHandlers.event.read("event", "eventName")

        self.position = self.configHandlers.event.read(self.machineId, "machinePosition")
        self.formatnum = self.configHandlers.machine.read("formatepc", "formatnum", "string", None)
        self.hardwaretype = self.configHandlers.machine.read('machine', 'hardwareType')

        self.battery = 0

        # 数据文件对象
        self.recordFile = recordFileHandle.RecordFileHandle()
        # 下载配置文件成功的标志位
        self.hasdownloadEventConfigFile = False
        # 网络服务器
        self.serverLocation = self.configHandlers.machine.read("server", "serverLocation",
                                                               "string", "China")
        if self.serverLocation=="China":
            self.serverLocation =os.environ.get("platform4_serverLocation")
        if self.serverLocation=="Customize 1":
            self.serverLocation ="https://feibot.tropevent.com/time/api/"
        if self.serverLocation[-1] != '/':
            self.serverLocation += "/"
        self.cpDataUploadUrl = self.configHandlers.machine.read("server", "cpDataUploadUrl")

        # 备用服务器
        self.server2Enabled = self.configHandlers.machine.read("server2", "enable", "int", 0)
        self.server2TimeFormat = self.configHandlers.machine.read("server2", "timeFormat")
        self.server2Location = self.configHandlers.machine.read("server2", "serverLocation", "string", "")
        if self.server2Location=="China":
            self.server2Location =os.environ.get("platform4_serverLocation")
        if self.server2Location=="Customize 1":
            self.server2Location ="https://feibot.tropevent.com/time/api/"
        if len(self.server2Location) > 5 and self.server2Location[-1] != '/':
            self.server2Location += "/"
        if len(self.server2Location) <= 5:
            self.server2Enabled = 0
        self.server2CpDataUploadUrl = self.configHandlers.machine.read("server2", "cpDataUploadUrl")
        self.server2TimeZone = self.configHandlers.machine.read("server2", "timeZone", "string", "+0")

        # post
        self.postEnabled = self.configHandlers.machine.read("post", "enable", "int", 0)
        self.postUrl = self.configHandlers.machine.read("post", "url")
        self.apiName = self.configHandlers.machine.read("post", "apiName")
        self.apiValue = self.configHandlers.machine.read("post", "apiValue")

        # RaceTec
        self.FeibotEnable = self.configHandlers.machine.read("server", "enable", "int", 0)
        self.raceTecEnable = self.configHandlers.machine.read("RaceTec", "enable", "int", 0)
        self.raceTecKey = self.configHandlers.machine.read("RaceTec", "APIkey")
        # Racemap
        self.racemapEnable = self.configHandlers.machine.read("Racemap", "enable", "int", 0)
        # RUFUS
        self.rufusEnable = self.configHandlers.machine.read("Rufus", "enable", "int", 0)
        self.rufusKey = self.configHandlers.machine.read("Rufus", "APIkey")
        self.rufusapi = None
        # 从配置文件中读出时间间隔
        self.uploadDataIntervalSecond = max(2, self.configHandlers.machine.read("IO", "uploadDataIntervalSecond", "int",
                                                                                5))

        self.uploadDataFileIntervalSecond = max(300,
                                                self.configHandlers.machine.read("IO", "uploadDataFileIntervalSecond",
                                                                                 "int", 180))

        # 每次上传到服务器最多芯片条数
        self.maxTagNumPerWebRequest = self.configHandlers.machine.read("IO", "maxTagNumPerWebRequest", "int", 200)

        # 使用ISO date string的标志
        # 重新调整存储格式
        self.useIsoDateTimeString = self.configHandlers.machine.read("IO", "useIsoDateTimeFormat", "string", "")
        self.isoTimeZoneString = self.configHandlers.machine.read("IO", "isoTimeZoneString", "string", "")
        self.dateTimeReformat = self.configHandlers.machine.read("IO", "dateTimeReformat", "string", "")
        self.recordFileFormat = self.configHandlers.machine.read("IO", "recordFileFormat", "string", ":")

        # 当前是否能上网
        self.serverConnection = 'disconnected'

        # 启动事件引擎
        self.localEventEngine = threading.Thread(target=self.eventEngine)
        self.localEventEngine.start()

        # 待保存和上传的数据
        self.data2File = []
        self.data2Web = []
        self.data2WebTemp = []  # 上传数据暂存列表

        # 备用的数据服务器
        self.data2Web2 = []
        self.data2Web2Temp = []

        # 需要上传数据文件的标志
        self.dataFileNeedUploadFlag = True

        # socket辅助
        self.socketBuilder = gtySocket.socketTools.SocketBuild()

        # 启动时只执行一次的log操作
        self.sendEvent('IO', 'file_autoDeleteIOFiles')

        # rewindData的变量
        self.rewindDataList = None
        self.hasGetGitNewVersion = False

    # 这里是一些自发的事件
    def start(self):
        localCounter = 0
        while True:
            localCounter += 1
            # 每1秒将数据存入文件
            self.sendEvent('IO', 'file_writeTagsToFile')

            # 刷新待传数据个数
            if self.server2Enabled == 1:
                self.sendEvent('UI', 'ui_uploadTagNumInWait', str(len(self.data2Web)) + "|" + str(len(self.data2Web2)))
            else:
                self.sendEvent('UI', 'ui_uploadTagNumInWait', str(len(self.data2Web)))

            # 联网时工作
            if self.serverConnection == 'connected' :
                # 数据上传到服务器
                if localCounter % self.uploadDataIntervalSecond == 1:
                    self.sendEvent('IO', 'web_uploadDataToServer')

                if self.hasGetGitNewVersion == False:
                    self.sendEvent('IO', 'io_getGitNewVersion')

                # 文件上传到服务器，第一次上传全部，往后只上传新增  #25.2.17改由每20s改为5分钟
                if localCounter % self.uploadDataFileIntervalSecond == 20 and self.dataFileNeedUploadFlag:
                    self.sendEvent('IO', 'web_uploadFileToServer', [False])
                    self.dataFileNeedUploadFlag = False

                # 每10分钟检测一次新版本状态
                if localCounter % 600 == 3:
                    self.sendEvent('IO', 'web_getNewestSoftwareVersion')

                #检查标志位，如果没下载过配置文件，则下载，会在下载成功后通过事件‘io_downloadEventConfigFileSuccess’修改标志位为True
                if self.hasdownloadEventConfigFile == False:
                    self.sendEvent('IO', 'web_downloadEventConfigFileFromServer')


            time.sleep(1)

    '''
    以下是事件处理引擎，用来处理事件结果
    '''

    # 事件处理引擎
    def eventEngine(self):
        while True:
            time.sleep(0.001)
            if not self.eventQ["IO"].empty():  # 事件队列非空
                try:
                    self.handleEventResult(self.eventQ["IO"].get(block=True, timeout=1))
                except Exception as e:
                    print(__file__, 'eventEngine error', e, traceback.extract_stack())
            else:
                pass

    # 发生事件
    def sendEvent(self, task, eventName, eventData=None):
        if eventData is None:
            eventData = []
        e = [eventName, eventData]
        try:
            if task.upper() in self.eventQ.keys():
                self.eventQ[task.upper()].put(e)
        except Exception as e:
            print(e)

    # 处理事件
    def handleEventResult(self, event):
        try:
            # 这里一共有以下一些事件
            task = event[0]
            if len(event) > 1:
                data = event[1]
            else:
                data = []

            if task == 'io_sendHeartBeatToStm32':
                self.sendEvent('UART', 'uart_sendHeartBeatToStm32')
                return
            if task == 'io_addTags':
                self.addTags(data)
                return
            if task == 'file_loadEventConfigFile':  # 更新存储的文件地址
                self.recordFile.updateRecordFileUrl(data)
                # 这两个客户要求保存在txt
                if self.formatnum in ["format4", "format5"]:
                    self.recordFile.updateRecordFileUrlTXT(data)
                self.sendEvent('UI', 'ui_recordFileUrl', [self.recordFile.recordFileUrl])
                return
            if task == 'file_writeTagsToFile':
                self.writeTagsToFile()
                return
            if task == 'web_uploadDataToServer':
                self.uploadDataToServer()
                return
            if task == 'io_uploadToServerSuccess':
                self.uploadDataToServerSuccess()
                return
            if task == 'io_uploadToServer2Success':
                self.uploadDataToServer2Success()
                return
            if task == 'io_internetConnectionReport':
                self.updateInternetConnection(data)
                return
            if task == 'web_uploadFileToServer':
                self.uploadFileToServer(data[0])
                return
            if task == 'io_checkServerConnection':
                self.testWebConnection(event)
                return
            if task == 'web_downloadEventConfigFileFromServer':
                self.downloadConfigFile(event)
                return
            if task == 'io_deleteDataFile':
                self.deleteDataFile()
                return
            if task == 'file_packDataSection':
                try:
                    self.sendEvent('UI', 'ui_socketSendPackDataResult', self.packDataSection(data))
                except Exception as e:
                    print(__file__, e)
                    self.sendEvent('UI', 'ui_socketSendPackDataResult', 'failed')
                return
            if task == 'io_rewindData':
                try:
                    self.startRewindData(data)
                except Exception as e:
                    print(__file__, e)
                return
            if task == 'io_rewindDataByLine':
                self.rewindDataByLine()
                return
            if task == 'io_oldFileUploadSuccessfully':
                self.oldFileUploadSuccessfully(data)
                return
            if task == 'file_autoDeleteIOFiles':
                self.deleteLogNDaysBefore()
                return
            if task == 'io_uploadAllLogFiles':
                self.uploadLogFiles(data)
                return
            if task == 'io_getGitNewVersion':
                self.hasGetGitNewVersion = True
                self.getGitNewVersionThread()
                return
            if task == 'io_downloadEventConfigFileSuccess':
                self.hasdownloadEventConfigFile = True
                # 重新载入配置处理器以更新event指向的文件，并更新eventId
                self.configHandlers = gtyTypes.ConfigHandlers()
                self.eventId = self.configHandlers.event.read("event", "eventId")
                return
            if task == 'io_recoradGunTime':
                self.recordGunTime(data)
                return

        except Exception as e:
            print(e)
            traceback.print_exc()

    '''
    =======================================================================================================================
    事件处理函数
    =======================================================================================================================
    '''

    def addTags(self, newTags):
        self.data2Web.extend(newTags)
        # 处理待传数据过长的问题
        if len(self.data2Web) > 300000:
            self.data2Web = self.data2Web[50000:]
        self.data2File.extend(newTags)
        self.dataFileNeedUploadFlag = True
        # 对于备用服务器
        if self.server2Enabled == 1:
            self.data2Web2.extend(newTags)
            if len(self.data2Web2) > 300000:
                self.data2Web2 = self.data2Web2[50000:]

    def writeTagsToFile(self):
        if len(self.data2File) == 0:
            return
        data2FileCopy = self.data2File.copy()
        localTagList = []
        fileUrl = self.recordFile.getRecordFileUrl()
        fileUrlTXT = self.recordFile.getRecordFileUrlTXT()
        # 复制出需要写入文件的数据
        for i in data2FileCopy:
            if self.recordFile.lineNumNow + 1 <= self.recordFile.maxLineNum:
                self.recordFile.lineNumNow += 1
                localTagList.append(i)
                self.data2File.pop(0)
            else:
                # 创建新文件
                self.recordFile.updateRecordFileUrl(True)
                if self.formatnum in ["format4", "format5"]:
                    self.recordFile.updateRecordFileUrlTXT(True)
                break

        if self.useIsoDateTimeString != "1":
            t = Process(target=gtyIoTools.recordEpcDefaultFormat,
                        args=(fileUrl, localTagList, self.dateTimeReformat, self.recordFileFormat))
            if self.formatnum == "format4":
                p = Process(target=gtyIoTools.recordEpcTXTKoji, args=(fileUrlTXT, localTagList))
                p.start()
            elif self.formatnum == "format5":
                p = Process(target=gtyIoTools.recordEpcTxtAaron, args=(fileUrlTXT, localTagList))
                p.start()
            # 客户Chris要求格式
            # eg:14d80692,2025-03-31_04:43:54
            elif self.formatnum in[ "format2", "format8"]:
                t = Process(target=gtyIoTools.recordEpcChris, args=(fileUrl, localTagList))
        else:
            t = Process(target=gtyIoTools.recordEpcISOFormat, args=(fileUrl, localTagList, self.isoTimeZoneString))
        t.start()

    # 上传数据到服务器,采用多进程
    def uploadDataToServer(self):
        if len(self.data2WebTemp) == 0:
            self.data2WebTemp = self.data2Web[0:min(len(self.data2Web), self.maxTagNumPerWebRequest)]
        # 上传到feibot国内外/wiclax/tropevent/time_data_cn后台（Get方法）
        if self.FeibotEnable == 1:
            a = Process(target=self.uploadDataToServerGet, args=(self.data2WebTemp,))
            a.start()
        # 上传到RaceTec
        if self.raceTecEnable == 1:
            t = Process(target=self.uploadDataToServerPostRacetec, args=(self.data2WebTemp,))
            t.start()
        # 上传到Racemap
        if self.racemapEnable == 1:
            l = Process(target=self.uploadDataToServerPostRacemap, args=(self.data2WebTemp,))
            l.start()
        # 上传到RUFUS
        if self.rufusEnable == 1:
            s = Process(target=self.uploadDataToServerPostRufus, args=(self.data2WebTemp,))
            s.start()
        # POST上传到feibot
        if self.postEnabled == 1:
            p = Process(target=self.uploadDataToServerPostFeibot, args=(self.data2WebTemp,))
            p.start()
        # 对于备用服务器
        if self.server2Enabled == 1:
            if len(self.data2Web2Temp) == 0:
                self.data2Web2Temp = self.data2Web2[0:min(len(self.data2Web2), self.maxTagNumPerWebRequest)]
            q = Process(target=self.uploadDataToServer2Get, args=(self.data2Web2Temp,))
            q.start()

    def uploadFileToServer(self, allFiles=False):
        t = Process(target=gtyIoTools.uploadResultFileToServer, args=(self.eventQ, self.recordFile, allFiles))
        t.start()

    def testWebConnection(self, event):
        data = event[1]
        self.battery = data[0]["batteryPercent"]
        # 检测与服务器之间的连接
        t = Process(target=gtyIoTools.testWebConnection,
                    args=(self.eventQ,
                          data[0]["machineId"],
                          data[0]["batteryPercent"],
                          data[0]["totalEpcRead"],
                          data[0]["differentEpcRead"],
                          data[0]["reader1Working"],
                          data[0]["reader2Working"],
                          data[0]["hardwareTime"],
                          data[0]["eventId"],
                          data[0]["softwareBranch"],
                          data[0]["softwareCommitId"],
                          data[0]["firmwareVersion"]))
        t.start()
        # 检测与备用服务器之间的连接
        if self.configHandlers.machine.read("server2", "enable", "string", "") == "1":
            print(__file__, "testWebConnection2")
            t2 = Process(target=gtyIoTools.testWebConnection2,
                         args=(self.eventQ,
                               data[0]["machineId"],
                               data[0]["batteryPercent"],
                               data[0]["totalEpcRead"],
                               data[0]["differentEpcRead"],
                               data[0]["reader1Working"],
                               data[0]["reader2Working"],
                               data[0]["hardwareTime"],
                               data[0]["eventId"]))
            t2.start()
        return

    # 下载配置文件
    def downloadConfigFile(self, event):
        t = Process(target=gtyIoTools.downloadFileFromServer,
                    args=(self.eventQ['UI'], event[1], self.machineId, self.eventQ['IO']))  # 这里最后一个参数是设备代码
        t.start()
        return

    # 截取文件时间区间并复制到http目录下，完成后发送完成socket
    def packDataSection(self, timeStampStartAndEnd):
        return self.recordFile.packDataSection(timeStampStartAndEnd)

    # 开始逐行发送数据
    def startRewindData(self, timeStampStartAndEnd):
        res, info, dataList = self.recordFile.getDataSection(timeStampStartAndEnd)
        print(__file__, res, info, len(dataList))
        if res:
            self.rewindDataList = dataList
            self.sendEvent('IO', 'io_rewindDataByLine', [])

    # 通过socket发送一条数据
    def rewindDataByLine(self):
        if len(self.rewindDataList) > 0:
            try:
                line = self.rewindDataList[0]
                del self.rewindDataList[0]
                parts = line.replace(":", ",").split(",")
                epc = parts[0]
                dateTimeString = parts[1] + ":" + parts[2] + ":" + parts[3]
                if len (parts) >=6 and self.formatnum in ["format2", "format8"]:
                    dataStr = parts[1].split(" ")[0]
                    hourStr = parts[1].split(" ")[1]
                    dateTimeString = dataStr+'_'+hourStr+':'+parts[2]+':'+parts[3]
                    port = parts[4]
                    match = re.search(r'\d+', port)
                    if match:
                        port = int(match.group())
                    else:
                        port = 0
                    readerid = str ((int(port)-1)//4 + 1)
                    rssi = parts[5]
                    socketString = dateTimeString + ',' + epc + ',' + readerid + ',' + str(port) + ',' + rssi
                else:
                    socketString = dateTimeString + ',' + epc
                self.sendEvent('SOCKET', 'socket_send', ['epc', socketString])
            except Exception as e:
                print(e)
            time.sleep(0.01)
            self.sendEvent('IO', 'io_rewindDataByLine', [])

    # 删除数据文件
    def deleteDataFile(self):
        self.recordFile.deleteDataFile()

    # 记录已经上传的数据文件
    def oldFileUploadSuccessfully(self, fileUrl):
        self.recordFile.fileUrlUploadedDone(fileUrl)

    '''
    =======================================================================================================================
    此类中的具体的工作函数
    =======================================================================================================================
    '''

    # 自动重发post请求
    def make_request_with_retries(self, url, payload, headers, content_type='text', max_retries=5, backoff_factor=1):
        retries = 0
        # 最多重试5次
        while retries < max_retries:
            try:
                if content_type == 'json':
                    response = requests.post(url, headers=headers, json=payload)
                elif content_type == 'text':
                    response = requests.post(url, headers=headers, data=payload.encode('utf-8'))
                else:
                    response = requests.post(url, headers=headers, data=payload.encode('utf-8'))

                # 判断返回状态码
                if response.status_code == 200:
                    gtyLog.log.write(__file__, 'response', response)
                    return response
                elif response.status_code in [429, 500, 502, 503, 504]:  # 临时错误或速率限制
                    gtyLog.log.write(__file__, f'response error , retry {retries + 1}', response)  # 错误记录日志
                    # 重发等待时间为基础系数*2^重试次数
                    sleep_time = backoff_factor * (2 ** retries)
                    time.sleep(sleep_time)
                    retries += 1
                    continue
                else:
                    gtyLog.log.write(__file__, 'response error', response)
                    return response
            except requests.exceptions.RequestException as e:
                gtyLog.log.write(__file__, 'RequestException', e)
                sleep_time = backoff_factor * (2 ** retries)
                time.sleep(sleep_time)
                retries += 1
        return None

    # feibot国内外/wiclax/tropevent/time_data_cn后台 Get请求方式上传数据
    def uploadDataToServerGet(self, tagList):
        if len(tagList) == 0:
            return
        url = ''
        try:
            md5String = os.environ.get("platform4_md5String")
            if md5String is None:
                md5String = ""
            token = str(random.randint(10000000, 99999999))
            url = self.serverLocation + self.cpDataUploadUrl + '?machineId=' + self.machineId + '&eventId=' + str(
                self.eventId) + '&epcNum=' + str(len(tagList))
            localCounter = 0
            for i in tagList:
                if self.useIsoDateTimeString != "1":
                    url += "&" + str(
                        localCounter) + "=" + i.epcString + "," + i.hardwareDateString + " " + i.hardwareTimeString
                else:
                    url += "&" + str(
                        localCounter) + "=" + i.epcString + "," + i.hardwareDateString + "T" + i.hardwareTimeString \
                           + self.isoTimeZoneString
                localCounter += 1
            url += '&t=' + str(token) + '&sign=' + gtyIoTools.get_token(token, md5String)
            url = url.replace(' ', '~')
            res = urllib.request.urlopen(url)
            getData = res.read().decode('utf-8')
            gtyLog.log.write(__file__, url, getData)
            if 'ok' in str.lower(getData):
                self.sendEvent('IO', 'io_uploadToServerSuccess', [])
        except Exception as e:
            gtyLog.log.write(__file__, url, e)

    # Feibot Post请求方式上传数据
    def uploadDataToServerPostFeibot(self, tagList):
        # 获取起点终点
        locations = self.position.split('|')
        location = locations[0]
        if len(tagList) == 0:
            return
        url = ''
        try:
            url = self.postUrl
            print(url)
            headers = {'Content-Type': 'application/json'}
            headers[self.apiName] = self.apiValue
            print('headers', headers)
            data = []
            for i in tagList:
                d = {}
                d['readerId'] = self.machineId
                d['location'] = location
                d['chipId'] = i.epcString
                if self.useIsoDateTimeString != "1":
                    d['timestamp'] = f'{i.hardwareDateString}T{i.hardwareTimeString}Z'
                else:
                    d['timestamp'] = f'{i.hardwareDateString}T{i.hardwareTimeString}{self.isoTimeZoneString}'
                d['battery'] = self.battery
                d['channel'] = i.channelId
                data.append(d)
            data = json.dumps(data, indent=4)
            print('payload', data)
            gtyLog.log.write(__file__, 'payload', data)
            # response = requests.request("POST", url, headers=headers, data=data)
            response = self.make_request_with_retries(url, data, headers, content_type='text')
            print('response', response.text)
            gtyLog.log.write(__file__, 'response', response)
            gtyLog.log.write(__file__, 'response.text', response.text)
        except Exception as e:
            gtyLog.log.write(__file__, url, e)

    # Feibot Post请求方式上传数据，之前用过，目前暂时不用
    def uploadDataToServerPostFeibot2(self, tagList):
        # 获取起点终点
        if len(tagList) == 0:
            return
        url = ''
        try:
            md5String = os.environ.get("platform4_md5String")
            if md5String is None:
                md5String = ""
            url = self.serverLocation + "post/" + self.cpDataUploadUrl
            # print(url)
            gtyLog.log.write(__file__, 'post url', url)
            headers = {'Content-Type': 'application/json'}
            # print('headers', headers)
            token = str(random.randint(10000000, 99999999))
            data = {
                "t": token,
                "sign": gtyIoTools.get_token(token, md5String),
                "machineId": self.machineId,
                "eventId": str(self.eventId),
                "epcNum": str(len(tagList)),
                "data": []
            }

            for i in tagList:
                data["data"].append({"epc": i.epcString, "times": i.hardwareDateString + '~' + i.hardwareTimeString})

            data = json.dumps(data, indent=4)
            print('payload', data)
            gtyLog.log.write(__file__, 'payload', data)
            # response = requests.request("POST", url, headers=headers, data=data)
            response = self.make_request_with_retries(url, data, headers, content_type='text')
            print('response', response.text)
            gtyLog.log.write(__file__, 'response', response)
            gtyLog.log.write(__file__, 'response.text', response.text)
            if 'ok' in str.lower(response.text):
                self.sendEvent('IO', 'io_uploadToServerSuccess', [])
        except Exception as e:
            gtyLog.log.write(__file__, url, e)

    # feibot 备用服务器Get请求方式
    def uploadDataToServer2Get(self, tagList):
        if self.server2Enabled == 0:
            return
        if len(tagList) == 0:
            return
        url = ''
        try:
            md5String = os.environ.get("platform4_md5String")
            if md5String is None:
                md5String = ""
            token = str(random.randint(10000000, 99999999))
            url = self.server2Location + self.server2CpDataUploadUrl + '?machineId=' + self.machineId + '&eventId=' + str(
                self.eventId) + '&epcNum=' + str(len(tagList))

            localCounter = 0
            for i in tagList:
                if self.server2TimeFormat == 'withTimeZone':
                    url += "&" + str(
                        localCounter) + "=" + i.epcString + "," + i.hardwareDateString + "T" + i.hardwareTimeString.replace(
                        ':', "%3A") \
                           + self.server2TimeZone.replace(':', "%3A")
                else:
                    url += "&" + str(
                        localCounter) + "=" + i.epcString + "," + i.hardwareDateString + " " + i.hardwareTimeString
                localCounter += 1

            url += '&t=' + str(token) + '&sign=' + gtyIoTools.get_token(token, md5String)

            url = url.replace(' ', '~')
            # url = url.replace('+', "%2B")
            # url = url.replace(',', "%2C")
            res = urllib.request.urlopen(url)
            getData = res.read().decode('utf-8')
            gtyLog.log.write(__file__, url, getData)
            if 'ok' in str.lower(getData):
                self.sendEvent('IO', 'io_uploadToServer2Success', [])
        except Exception as e:
            gtyLog.log.write(__file__, url, e)

    # RaceTec post上传数据
    def uploadDataToServerPostRacetec(self, tagList):
        # 获取起点终点
        locations = self.position.split('|')
        location = locations[0]
        if len(tagList) == 0:
            return
        url = ''
        try:
            url = "https://api.racetec.online/RDR/AddChipTime.aspx"
            filename = './Newevent.ini'
            with open(filename, 'r') as f:
                new_event_id = f.readline().strip()  # 去掉换行符，否则会读到一个换行符导致参数不为空
                f.close()
            if new_event_id:
                payloadHead = f'ApiKey=27363534,ReaderId={self.machineId},RaceId={new_event_id},Provider=Feibot,ClientId={self.raceTecKey}'
                gtyLog.log.write(__file__, 'RaceId已更改', new_event_id)
            else:
                payloadHead = f'ApiKey=27363534,ReaderId={self.machineId},RaceId={self.eventId},Provider=Feibot,ClientId={self.raceTecKey}'
                gtyLog.log.write(__file__, 'RaceId未更改', self.eventId)
            payload = '{' + payloadHead + '}'
            headers = {'Content-Type': 'text/plain', 'charset': 'utf-8;'}
            localCounter = 0
            for i in tagList:
                if self.hardwaretype == 'A400':
                    data = f'Location={location},ChipCode={i.epcString},ChipTime={i.hardwareDateString} {i.hardwareTimeString},Bat={i.bat}'
                    payload += '{' + data + '}'
                else:
                    data = f'Location={location},ChipCode={i.epcString},ChipTime={i.hardwareDateString} {i.hardwareTimeString}'
                    payload += '{' + data + '}'
            print('payload', payload)
            gtyLog.log.write(__file__, 'RaceTec payload', payload)
            # response = requests.request("POST", url, headers=headers, data=payload)
            response = self.make_request_with_retries(url, payload, headers)
            print('response', response.text)
            gtyLog.log.write(__file__, 'RaceTec response', response.text)
            if "Count" in response.text:  # 上传成功会返回 {Count=？},？代表多少个数量
                self.sendEvent('IO', 'io_uploadToServerSuccess', [])
        except Exception as e:
            gtyLog.log.write(__file__, url, e)

    # Racemap post上传数据
    def uploadDataToServerPostRacemap(self, tagList):
        locations = self.position.split('|')
        location = locations[0]
        if len(tagList) == 0:
            return
        url = ''
        try:
            url = 'https://racemap.com/services/trackping/api/v1/timing_input/pings'
            headers = {
                'Content-Type': 'application/json',
                'api-token': 'RR4GEXMZ'
            }
            data = []
            for i in tagList:
                d = {}
                d['timingId'] = self.machineId
                d['timingName'] = location
                d['chipId'] = i.epcString
                if self.useIsoDateTimeString != "1":
                    # 默认本地时间位UTC时间
                    d['timestamp'] = f'{i.hardwareDateString}T{i.hardwareTimeString}Z'
                else:
                    # 转化成utc时间
                    timestamp = f'{i.hardwareDateString}T{i.hardwareTimeString}{self.isoTimeZoneString}'
                    localTime = datetime.fromisoformat(timestamp)
                    utcTime = localTime.astimezone(timezone.utc)
                    utcTimeStr = utcTime.strftime('%Y-%m-%dT%H:%M:%S.%f')[:-3] + 'Z'
                    d['timestamp'] = utcTimeStr
                data.append(d)
            data = json.dumps(data)
            print('racemap payload', data)
            gtyLog.log.write(__file__, 'Racemap payload', data)
            response = requests.request("POST", url, headers=headers, data=data)
            print('racemap response', response.text)
        except Exception as e:
            gtyLog.log.write(__file__, url, e)

    # RaceTime post上传数据
    def uploadDataToServerPostRacetime(self, tagList):
        if len(tagList) == 0:
            return
        url = ''
        try:
            url = 'https://api-racetime-development.trackify.life/api/public/chip-read-raw'
            print(url)
            headers = {
                'Content-Type': 'application/json',
            }

            for i in tagList:
                data = {
                    "data": {
                        "antenna": 0,
                        "hostName": self.machineId,
                        "id_chipReadTable": "0",
                        "idHex": i.epcString,
                        "channel": 0,
                        "format": 0,
                        "reads": 0,
                        "bib": "0",
                        "eventNum": 0,
                        "peakRssi": 0
                    },
                    "type": "NORMAL",
                    "timestamp": f'{i.hardwareDateString}T{i.hardwareTimeString}'
                }
                body = json.dumps(data)
                print('payload', body)
                response = requests.request("POST", url, headers=headers, data=body)
                print('response', response.text)
        except Exception as e:
            gtyLog.log.write(__file__, url, e)

    # Rufus post上传数据
    def uploadDataToServerPostRufus(self, tagList):
        #初始化api接口
        if self.rufusapi == None:
            try:
                locations = self.position.split('|')
                if len(locations) < 2:
                    # 给默认值
                    loc1 = locations[0] if len(locations) > 0 else ""
                    loc2 = ""  # 默认第二个位置为空
                else:
                    loc1, loc2 = locations[0], locations[1]
                self.rufusapi = RUFUSapi.RUFUSCloudAPI(self.rufusKey,self.machineId,self.eventId,"",self.hardwaretype,loc1,
                                                  loc2,self.eventname)
            except Exception as e:
                gtyLog.log.write("RUFUSAPI init error: " + str(e))

        if len(tagList) == 0:
            return
        try:
            temptagList=[]
            for i in tagList:
                temptagList.append([i.epcString,f"{i.hardwareDateString}T{i.hardwareTimeString}Z"])
            self.rufusapi.add_passing_record(temptagList)
        except Exception as e:
            gtyLog.log.write(__file__, 'Rufus upload error', e)
        pass
    # 主服务器
    def uploadDataToServerSuccess(self):
        self.data2Web = self.data2Web[len(self.data2WebTemp):]
        self.data2WebTemp = []

    # 备用服务器
    def uploadDataToServer2Success(self):
        self.data2Web2 = self.data2Web2[len(self.data2Web2Temp):]
        self.data2Web2Temp = []

    # 将硬件时间转化位unix时间戳
    def turnUnixTimeStamp(self, date_str, time_str):
        datetime_str = f"{date_str} {time_str}"

        # 解析字符串为 datetime 对象
        dt_obj = datetime.strptime(datetime_str, "%Y-%m-%d %H:%M:%S.%f")

        # 转换为 Unix 时间戳（秒级）
        unix_timestamp_seconds = dt_obj.timestamp()

        # 转换为毫秒级时间戳
        unix_timestamp_milliseconds = int(unix_timestamp_seconds * 1000)

        return unix_timestamp_milliseconds

    # 更新结果
    def updateInternetConnection(self, data):
        self.serverConnection = data

    # 删除超过n天的日志文件
    def deleteLogNDaysBefore(self):
        try:
            en = self.configHandlers.machine.read('machine', 'autoDeleteLogFile', 'int', 1)
            if en == 0:
                return
            n = self.configHandlers.machine.read('machine', 'autoDeleteLogFileBeforeNDays', 'int', 30)
            if n < 7:
                n = 7
            logFiles = os.listdir(systemConfig.param.logFilePath)
            for logfile in logFiles:
                parts = logfile.replace('-', '_').replace('.', '_').split('_')
                logDate = datetime.datetime(int(parts[1]), int(parts[2]), int(parts[3]),
                                            int(parts[4]), int(parts[5]), int(parts[6]))
                days = datetime.datetime.now() - logDate
                if days.days > n:
                    os.system('rm ' + os.path.join(systemConfig.param.logFilePath, logfile))
                    print('delete log file: ', logfile)
        except Exception as e:
            print(e)

    # 上传日志文件到oss服务器
    def uploadLogFiles(self, data):
        t = Process(target=gtyIoTools.uploadLogFilesToOss, args=(self.eventQ, data))
        t.start()

    # 多线程方式获取git仓库的最新版本
    def getGitNewVersion(self):
        gitResult = gtyTools.gitTools.gitListRemoteVersion()
        self.sendEvent('UI', 'ui_getGitNewVersion', gitResult)

    def getGitNewVersionThread(self):
        t1 = threading.Thread(target=self.getGitNewVersion)
        t1.start()

    def recordGunTime(self, guntime):
        guntag = gtyTypes.tag()
        guntag.epcString = '0'
        guntag.rssi = '0'
        guntag.hardwareTime=guntime
        guntag.buildTimeString()
        self.data2File.append(guntag)
        pass


def main(eventQ):
    while True:
        print("===================IO task started===================")
        try:
            io = GtyIO(eventQ)
            io.start()
        except Exception as e:
            traceback.print_exc()
            gtyLog.log.write(__file__, e, '=================IO service restart!=================')
        time.sleep(1)
